import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_lui/flutter_lui.dart';
import 'package:flutter_lui/src/l_style.dart';

///  默认高度
final _defaultHeight = lrpx(200);

/// 滚动时间 ms
const int _animationTime = 200;

/// 每次切换的时间间隔 ms
const int _sleepTime = 3000;

/// 指示器区域默认高度
final _defaultIndicatorBoxHeight = lrpx(20);

/// 轮播图组件
class LCarousel extends StatefulWidget {
  /// 宽度
  final double? width;

  /// 高度
  final double? height;

  /// 默认索引
  final int? defaultIndex;

  /// 子组件
  final List<Widget> children;

  /// 是否自动播放
  final bool autoPlay;

  /// 每次切换的时间间隔 ms
  final int sleepTime;

  /// 滚动动画时间 ms
  final int animationTime;

  ///  滚动方向 也决定了指示器是横向的还是纵向的
  final Axis scrollDirection;

  /// 是否从头到尾滚动，否则是从尾到头滚动
  final bool toEnd;

  /// 是否显示页数指示器
  final bool showIndicator;

  /// 指示器的位置
  /// 1，指定指示器区域帖在哪个边或者在中间
  /// 2，指定指示器聚集的方位
  final Alignment? indicatorPosition;

  /// 指示器 配置
  final LIndicatorConfig? dotIndicatorConfig;

  /// 切换回调
  final void Function(int)? onChange;
  final void Function(LCarouselController)? onCreated;
  const LCarousel(
      {this.width,
      this.height,
      this.defaultIndex,
      this.autoPlay = true,
      this.sleepTime = _sleepTime,
      this.animationTime = _animationTime,
      this.onChange,
      this.showIndicator = true,
      this.scrollDirection = Axis.horizontal,
      this.toEnd = true,
      this.indicatorPosition,
      this.dotIndicatorConfig,
      this.onCreated,
      required this.children,
      Key? key})
      : super(key: key);

  @override
  State<StatefulWidget> createState() => _LCarousel();
}

class _LCarousel extends State<LCarousel> {
  late List<Widget> children;
  late PageController pageController;
  late LCarouselController controller;
  late int currentIndex;
  Timer? timer;
  @override
  void initState() {
    super.initState();

    if (widget.children.isNotEmpty) {
      currentIndex = (widget.defaultIndex ??
              (widget.toEnd ? 0 : widget.children.length - 1)) +
          1;
      children = [
        widget.children.last,
        ...widget.children,
        widget.children.first,
      ];
    } else {
      currentIndex = 1;
      var defaultChild = LBox(
        width: widget.width ?? PageSize.width,
        height: widget.height ?? _defaultHeight,
        gradient: LinearGradient(colors: [
          Colors.black.withOpacity(0.1),
          Colors.transparent,
          Colors.black.withOpacity(0.1),
        ]),
      );
      children = [defaultChild, defaultChild, defaultChild];
    }
    pageController = PageController(initialPage: currentIndex);
    if (widget.autoPlay == true) {
      startTimer();
    }
    controller = LCarouselController(
        index: currentIndex,
        next: handleNext,
        prev: handlePrev,
        jumpTo: handeJumpTo);
  }

  @override
  void didUpdateWidget(LCarousel oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.children.isNotEmpty) {
      currentIndex = (widget.defaultIndex ??
              (widget.toEnd ? 0 : widget.children.length - 1)) +
          1;
      children = [
        widget.children.last,
        ...widget.children,
        widget.children.first,
      ];
    } else {
      currentIndex = 1;
      var defaultChild = LBox(
        width: widget.width ?? PageSize.width,
        height: widget.height ?? _defaultHeight,
        gradient: LinearGradient(colors: [
          Colors.black.withOpacity(0.1),
          Colors.transparent,
          Colors.black.withOpacity(0.1),
        ]),
      );
      children = [defaultChild, defaultChild, defaultChild];
    }
  }

  @override
  dispose() {
    super.dispose();
    pageController.dispose();
    controller.dispose();
  }

  startTimer() {
    timer = Timer(Duration(milliseconds: widget.sleepTime), () {
      if (!mounted) return;
      pageController.animateToPage(currentIndex + (widget.toEnd ? 1 : -1),
          duration: Duration(milliseconds: widget.animationTime),
          curve: Curves.ease);
    });
  }

  handleNext() {
    if (controller._dispose) return;
    if (timer?.isActive == true) {
      timer!.cancel();
    }
    if (!mounted) return;
    pageController.animateToPage(currentIndex + (widget.toEnd ? 1 : -1),
        duration: Duration(milliseconds: widget.animationTime),
        curve: Curves.ease);
  }

  handlePrev() {
    if (controller._dispose) return;
    if (timer?.isActive == true) {
      timer!.cancel();
    }
    if (!mounted) return;
    pageController.animateToPage(currentIndex + (widget.toEnd ? -1 : 1),
        duration: Duration(milliseconds: widget.animationTime),
        curve: Curves.ease);
  }

  handeJumpTo(int index) {
    if (controller._dispose) return;
    if (timer?.isActive == true) {
      timer!.cancel();
    }
    if (!mounted) return;
    pageController.animateToPage(index,
        duration: Duration(milliseconds: widget.animationTime),
        curve: Curves.ease);
  }

  handlePageChange(int idx) {
    if (timer?.isActive == true) {
      timer!.cancel();
    }

    if (idx <= 0) {
      idx = children.length - 2;
      Timer(Duration(milliseconds: widget.animationTime), () {
        if (!mounted) return;
        pageController.jumpToPage(idx);
      });
    } else if (idx >= children.length - 1) {
      idx = 1;
      Timer(Duration(milliseconds: widget.animationTime), () {
        if (!mounted) return;
        pageController.jumpToPage(idx);
      });
    }
    if (currentIndex != idx) {
      setState(() {
        currentIndex = idx;
        controller.setIndex(idx);
      });
      if (widget.onChange != null) {
        widget.onChange!(currentIndex - 1);
      }
    }
    if (widget.autoPlay == true) {
      startTimer();
    }
  }

  @override
  Widget build(BuildContext context) {
    double? useLeft = 0;
    double? useRight = 0;
    double? useTop = 0;
    double? useBottom = 0;
    if (widget.scrollDirection == Axis.horizontal) {
      if (widget.indicatorPosition == Alignment.topLeft ||
          widget.indicatorPosition == Alignment.topCenter ||
          widget.indicatorPosition == Alignment.topRight) {
        useBottom = null;
      } else if (widget.indicatorPosition == Alignment.centerLeft ||
          widget.indicatorPosition == Alignment.center ||
          widget.indicatorPosition == Alignment.centerRight) {
        useTop = ((widget.height ?? _defaultHeight) -
                (widget.dotIndicatorConfig?.height ??
                    _defaultIndicatorBoxHeight)) /
            2;
        useBottom = null;
      } else {
        useTop = null;
      }
    } else {
      if (widget.indicatorPosition == Alignment.topLeft ||
          widget.indicatorPosition == Alignment.centerLeft ||
          widget.indicatorPosition == Alignment.bottomLeft) {
        useRight = null;
      } else if (widget.indicatorPosition == Alignment.topCenter ||
          widget.indicatorPosition == Alignment.center ||
          widget.indicatorPosition == Alignment.bottomCenter) {
        useLeft = ((widget.width ?? PageSize.width) -
                (widget.dotIndicatorConfig?.height ??
                    _defaultIndicatorBoxHeight)) /
            2;
        useRight = null;
      } else {
        useLeft = null;
      }
    }
    return LPostFrameInfo(
      callback: (v) {
        if (widget.onCreated != null) {
          widget.onCreated!(controller);
        }
      },
      child: SizedBox(
        width: widget.width ?? PageSize.width,
        height: widget.height ?? _defaultHeight,
        child: Stack(
          children: [
            PageView(
                padEnds: false,
                scrollDirection: widget.scrollDirection,
                controller: pageController,
                onPageChanged: handlePageChange,
                children: children),
            widget.showIndicator
                ? Positioned(
                    left: useLeft,
                    right: useRight,
                    top: useTop,
                    bottom: useBottom,
                    child: LDotIndicator(
                      defaultConfig: _LIndicatorConfig(
                          toEnd: widget.toEnd,
                          time: widget.animationTime,
                          length: children.length - 2,
                          currentIndex: currentIndex - 1,
                          direction: widget.scrollDirection,
                          alignment:
                              widget.indicatorPosition ?? Alignment.center),
                      config: widget.dotIndicatorConfig,
                    ))
                : Container()
          ],
        ),
      ),
    );
  }
}

class _LIndicatorConfig {
  /// 滚动时间
  final int time;

  /// 指示器数量
  final int length;

  /// 当前下标
  final int currentIndex;

  /// 滚动方向 水平 或垂直
  final Axis direction;

  /// 指示器位置 按滚动方向排列后聚集于开始、中间、或结尾
  final Alignment alignment;

  /// 滚动方式 从头到尾滚动
  final bool toEnd;

  const _LIndicatorConfig({
    required this.time,
    required this.length,
    required this.currentIndex,
    required this.direction,
    required this.alignment,
    required this.toEnd,
  });
}

class LIndicatorConfig {
  /// 指示器区域和滚动方向的交叉方向的高度
  final double? height;

  /// 指示器区域背景颜色
  final Color? backgroundColor;

  /// 指示器宽度 宽高设置成一样的值可以做成方块或圆点
  final double? dotWidth;

  /// 指示器高度度 宽高设置成一样的值可以做成方块或圆点
  final double? dotHeight;

  /// 宽度是否变化 做成线条样式时开启此项，可做成长度伸缩 圆点或方块形状时关闭此项可只改变颜色
  final bool? dotWidthChanged;

  /// 指示器未激活时的颜色
  final Color? dotInactiveColor;

  /// 指示器激活时的颜色
  final Color? dotActiveColor;

  /// 指示器的圆角度 可做成圆点或方块形状
  final double? dotRadius;

  /// 指示器的间隔
  final double? dotSpace;
  const LIndicatorConfig(
      {this.height,
      this.backgroundColor,
      this.dotWidth,
      this.dotHeight,
      this.dotInactiveColor,
      this.dotActiveColor,
      this.dotRadius,
      this.dotSpace,
      this.dotWidthChanged = true});
}

/// 默认线条长度
final _defaultDotWidth = lrpx(30);

/// 默认线条宽度
final _defaultDotHeight = lrpx(3);

class LDotIndicator extends StatefulWidget {
  // ignore: library_private_types_in_public_api
  final _LIndicatorConfig defaultConfig;
  final LIndicatorConfig? config;

  // ignore: library_private_types_in_public_api
  const LDotIndicator({required this.defaultConfig, this.config, Key? key})
      : super(key: key);

  @override
  State<StatefulWidget> createState() => _LDotIndicator();
}

class _LDotIndicator extends State<LDotIndicator>
    with TickerProviderStateMixin {
  late AnimationController controller;
  double value = 1;
  @override
  void initState() {
    super.initState();
    controller = AnimationController(
        lowerBound: 0,
        upperBound: 1,
        vsync: this,
        duration: Duration(milliseconds: widget.defaultConfig.time));
    controller.addListener(() {
      setState(() {
        value = controller.value;
      });
    });
  }

  @override
  void didUpdateWidget(covariant LDotIndicator oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.defaultConfig.currentIndex !=
        widget.defaultConfig.currentIndex) {
      controller.reset();
      controller.forward();
    }
  }

  @override
  void dispose() {
    super.dispose();
    controller.dispose();
  }

  findOldIndex() {
    var oldIndex = 0;
    if (widget.defaultConfig.toEnd) {
      oldIndex = widget.defaultConfig.currentIndex - 1;
    } else {
      oldIndex = widget.defaultConfig.currentIndex + 1;
    }
    if (oldIndex < 0) {
      oldIndex = widget.defaultConfig.length - 1;
    }
    if (oldIndex >= widget.defaultConfig.length) {
      oldIndex = 0;
    }
    return oldIndex;
  }

  calcWidth(double width, int index) {
    if (widget.config?.dotWidthChanged == false) {
      return width;
    }
    var plusWidth = width / 3;

    if (index == widget.defaultConfig.currentIndex) {
      return width + plusWidth * value;
    }
    if (index == findOldIndex()) {
      return width + plusWidth * (1 - value);
    }
    return width;
  }

  calcOpacity(int index) {
    if (index == widget.defaultConfig.currentIndex) {
      return value.toDouble();
    }
    if (index == findOldIndex()) {
      return 1 - value;
    }
    return 0.0;
  }

  @override
  Widget build(BuildContext context) {
    late MainAxisAlignment useAlignment;
    if (widget.defaultConfig.direction == Axis.horizontal) {
      if (widget.defaultConfig.alignment == Alignment.topLeft ||
          widget.defaultConfig.alignment == Alignment.centerLeft ||
          widget.defaultConfig.alignment == Alignment.bottomLeft) {
        useAlignment = MainAxisAlignment.start;
      } else if (widget.defaultConfig.alignment == Alignment.topRight ||
          widget.defaultConfig.alignment == Alignment.centerRight ||
          widget.defaultConfig.alignment == Alignment.bottomRight) {
        useAlignment = MainAxisAlignment.end;
      } else {
        useAlignment = MainAxisAlignment.center;
      }
    } else {
      if (widget.defaultConfig.alignment == Alignment.topLeft ||
          widget.defaultConfig.alignment == Alignment.topCenter ||
          widget.defaultConfig.alignment == Alignment.topRight) {
        useAlignment = MainAxisAlignment.start;
      } else if (widget.defaultConfig.alignment == Alignment.bottomLeft ||
          widget.defaultConfig.alignment == Alignment.bottomCenter ||
          widget.defaultConfig.alignment == Alignment.bottomRight) {
        useAlignment = MainAxisAlignment.end;
      } else {
        useAlignment = MainAxisAlignment.center;
      }
    }
    var useBackgroundColor =
        widget.config?.backgroundColor ?? Colors.black.withOpacity(0.1);
    var useDotWidth = widget.config?.dotWidth ?? _defaultDotWidth;
    var useDotHeight = widget.config?.dotHeight ?? _defaultDotHeight;
    var useDotRadius = widget.config?.dotRadius ?? LStyle.borderRadius;
    var useDotInactiveColor =
        widget.config?.dotInactiveColor ?? LUI.instance.backgroundColor;
    var useDotActiveColor = widget.config?.dotActiveColor ?? Colors.white;

    var useHeight = widget.config?.height ?? _defaultIndicatorBoxHeight;
    return LBox(
      width:
          widget.defaultConfig.direction == Axis.horizontal ? null : useHeight,
      height:
          widget.defaultConfig.direction == Axis.vertical ? null : useHeight,
      backgroundColor: useBackgroundColor,
      direction: widget.defaultConfig.direction == Axis.horizontal
          ? BoxDirections.horizontal
          : BoxDirections.vertical,
      mainAxisAlignment: useAlignment,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: List.generate(
          widget.defaultConfig.length,
          (index) => LBox(
                margin: widget.defaultConfig.direction == Axis.horizontal
                    ? [0, (widget.config?.dotSpace ?? lrpx(10)) / 2]
                    : [(widget.config?.dotSpace ?? lrpx(10)) / 2, 0],
                width: widget.defaultConfig.direction == Axis.horizontal
                    ? calcWidth(useDotWidth, index)
                    : useDotHeight,
                height: widget.defaultConfig.direction == Axis.horizontal
                    ? useDotHeight
                    : calcWidth(useDotWidth, index),
                borderRadius: [useDotRadius],
                backgroundColor: index == widget.defaultConfig.currentIndex
                    ? useDotActiveColor
                    : useDotInactiveColor,
                child: Stack(children: [
                  LBox(
                    borderRadius: [useDotRadius],
                    backgroundColor: LUI.instance.backgroundColor,
                  ),
                  LBox(
                    borderRadius: [useDotRadius],
                    backgroundColor:
                        Colors.white.withOpacity(calcOpacity(index)),
                  )
                ]),
              )),
    );
  }
}

class LCarouselController {
  bool _dispose = false;

  /// 当前索引
  late int _index;

  /// 下一页
  Function() next;

  /// 上一页
  Function() prev;

  /// 跳转到某一页 超过索引会自动检测
  Function(int index) jumpTo;
  LCarouselController(
      {int index = 0,
      required this.next,
      required this.prev,
      required this.jumpTo}) {
    _index = index;
  }

  int get index => _index;

  setIndex(int idx) {
    _index = idx;
  }

  dispose() {
    _dispose = true;
  }
}
